/**
 * This file is released under the GNU General Public License.
 * Refer to the COPYING file distributed with this package.
 *
 * Copyright (c) 2008-2009 WURFL-Pro srl
 */
package net.sourceforge.wurfl.core.resource;

import java.io.Serializable;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.apache.commons.collections.IteratorUtils;
import org.apache.commons.collections.map.UnmodifiableMap;
import org.apache.commons.lang.Validate;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;

import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap;

/**
 * A Collection of ModelDevice.
 * 
 * <p>
 * This is a convenient class to store collection of ModelDevice.
 * </p>
 * 
 * @author WURFL-PRO SRL, Rome, Italy
 * @version $Id: ModelDevices.java 1045 2009-03-09 15:52:53Z filippo.deluca $
 */
public class ModelDevices implements Serializable {

	private static final long serialVersionUID = 10L;

	/** Delegate map of ModelDevice by String */
	private Map/* String,ModelDevice */devicesById = new ConcurrentHashMap();

	// Constructors *******************************************************
	
	/**
	 * Default constructor
	 */
	public ModelDevices() {
		// Empty
	}

	/**
	 * Constructor by other ModelDevices.
	 * 
	 * @param devices
	 *            source ModelDevices.
	 */
	public ModelDevices(ModelDevices devices) {
		this.devicesById.putAll(devices.devicesById);
	}

	/**
	 * Constructor by Map ModelDevice by Id string.
	 * 
	 * @param devices
	 *            Map<String, ModelDevice> as source.
	 */
	public ModelDevices(Map/* String,ModelDevice */devices) {

		Validate.notNull(devices);
		Validate.noNullElements(devices.values());
		Validate.allElementsOfType(devices.keySet(), String.class);
		Validate.allElementsOfType(devices.values(), ModelDevice.class);

		this.devicesById.putAll(devices);
	}

	/**
	 * Constructor by Collection of ModelDevice.
	 * 
	 * @param devices
	 *            Collection<ModelDevice> as source.
	 */
	public ModelDevices(Collection/* ModelDevice */devices) {

		Validate.notNull(devices);
		Validate.noNullElements(devices);
		Validate.allElementsOfType(devices, ModelDevice.class);

		Iterator it = devices.iterator();
		while (it.hasNext()) {
			ModelDevice device = (ModelDevice) it.next();
			this.devicesById.put(device.getID(), device);
		}
	}

	/**
	 * Constructor by ModelDevice array.
	 * 
	 * @param devices
	 *            array of ModelDevice as source.
	 */
	public ModelDevices(ModelDevice[] devices) {

		this(Arrays.asList(devices));
	}
	
	// Business methods ***************************************************

	/**
	 * Return the size of this collection.
	 * 
	 * @return
	 */
	public int size() {
		return devicesById.size();
	}

	/**
	 * Returns if this collection contains given device.
	 * 
	 * @param device
	 *            ModelDevice to check against.
	 * @return true if this collection contains given ModelDevice, false
	 *         otherwise.
	 */
	public boolean contains(ModelDevice device) {
		return devicesById.containsValue(device);
	}

	/**
	 * Returns if this collection contains device with given id.
	 * 
	 * @param id
	 *            ModelDevice id to check against.
	 * @return true if this collection contains device with given id, false
	 *         otherwise.
	 */
	public boolean containsId(String id) {
		return devicesById.containsKey(id);
	}

	/**
	 * Return the devices contained in this collection.
	 * 
	 * @return Set<ModelDevice> of devices contained by this collection.
	 */
	public Set/* ModelDevice */getDevices() {
		return new HashSet(devicesById.values());
	}

	/**
	 * Return the devices contained in this collection mapped by id.
	 * 
	 * @return Map<String,ModelDevice> of devices by id.
	 */
	public Map/* ModelDevice */getDevicesById() {
		return UnmodifiableMap.decorate(devicesById);
	}

	/**
	 * Return iterator of contained devices.
	 * 
	 * @return Iterator<ModelDevice> of contained devices.
	 */
	public Iterator/* ModelDevice */iterator() {
		return IteratorUtils.unmodifiableIterator(devicesById.values()
				.iterator());
	}

	/**
	 * Return contained device by id.
	 * 
	 * @param id
	 *            The id String of requested ModelDevice
	 * @return Contained ModelDevice identified by id
	 */
	public ModelDevice getById(String id) {
		return (ModelDevice) devicesById.get(id);
	}

	/**
	 * Add a device to collection.
	 * 
	 * @param device
	 *            ModelDevice to add.
	 */
	public void add(ModelDevice device) {
		devicesById.put(device.getID(), device);
	}

	/**
	 * Add devices to collection.
	 * 
	 * @param devices
	 *            Collection<ModelDevice> to add.
	 */
	public void addAll(Collection/* ModelDevice */devices) {

		Validate.notNull(devices);
		Validate.noNullElements(devices);
		Validate.allElementsOfType(devices, ModelDevice.class);

		Iterator it = devices.iterator();
		while (it.hasNext()) {
			ModelDevice device = (ModelDevice) it.next();
			this.devicesById.put(device.getID(), device);
		}
	}

	/**
	 * Add devices to collection.
	 * 
	 * @param devices
	 *            ModelDevices to add.
	 */
	public void addAll(ModelDevices devices) {

		this.devicesById.putAll(devices.devicesById);
	}

	/**
	 * Remove device from collection.
	 * 
	 * @param device
	 *            ModelDevice to remove.
	 */
	public void remove(ModelDevice device) {
		devicesById.remove(device.getID());
	}

	/**
	 * Remove given devices from collection.
	 * 
	 * @param devices
	 *            Collection<ModelDevice> to remove.
	 */
	public void removeAll(Collection/* ModelDevice */devices) {
		Iterator it = devices.iterator();
		while (it.hasNext()) {
			ModelDevice device = (ModelDevice) it.next();
			this.devicesById.remove(device.getID());
		}
	}

	/**
	 * Remove given devices from collection.
	 * 
	 * @param devices
	 *            ModelDevices to remove.
	 */
	public void removeAll(ModelDevices devices) {
		Iterator it = devices.iterator();
		while (it.hasNext()) {
			ModelDevice device = (ModelDevice) it.next();
			this.devicesById.remove(device.getID());
		}
	}

	/**
	 * Remove all contained devices.
	 */
	public void clear() {
		devicesById.clear();
	}
	
	// Commons methods ****************************************************
	
	public int hashCode() {
		HashCodeBuilder hb = new HashCodeBuilder();
		
		hb.append(getClass()).append(devicesById);
		
		return hb.toHashCode();
	}
	
	public boolean equals(Object obj) {
		EqualsBuilder eb = new EqualsBuilder();
		
		eb.appendSuper(getClass().isInstance(obj));
		if(eb.isEquals()){
			ModelDevices other = (ModelDevices)obj;
			eb.append(devicesById, other.devicesById);
		}
		
		return eb.isEquals();
	}

}
